# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.757.13.5 -> 1.757.13.6 # drivers/char/agp/agpgart_be.c 1.41.1.5 -> 1.41.1.6 # drivers/char/agp/agp.h 1.20 -> 1.21 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/10/29 bjorn_helgaas@hp.com 1.757.13.6 # Detect HP ZX1 AGP bridge via ACPI instead of the old, unmaintainable # "fake PCI device" scheme. # -------------------------------------------- # diff -Nru a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h --- a/drivers/char/agp/agp.h Wed Oct 8 09:08:03 2003 +++ b/drivers/char/agp/agp.h Wed Oct 8 09:08:03 2003 @@ -424,13 +424,15 @@ #define SVWRKS_POSTFLUSH 0x14 #define SVWRKS_DIRFLUSH 0x0c -/* HP ZX1 SBA registers */ -#define HP_ZX1_CTRL 0x200 +/* HP ZX1 IOC registers */ #define HP_ZX1_IBASE 0x300 #define HP_ZX1_IMASK 0x308 #define HP_ZX1_PCOM 0x310 #define HP_ZX1_TCNFG 0x318 #define HP_ZX1_PDIR_BASE 0x320 -#define HP_ZX1_CACHE_FLUSH 0x428 + +/* HP ZX1 LBA registers */ +#define HP_ZX1_AGP_STATUS 0x64 +#define HP_ZX1_AGP_COMMAND 0x68 #endif /* _AGP_BACKEND_PRIV_H */ diff -Nru a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c --- a/drivers/char/agp/agpgart_be.c Wed Oct 8 09:08:03 2003 +++ b/drivers/char/agp/agpgart_be.c Wed Oct 8 09:08:03 2003 @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -369,6 +370,9 @@ /* Routine to copy over information structure */ +/* AGP bridge need not be PCI device, but DRM thinks it is. */ +static struct pci_dev fake_bridge_dev; + int agp_copy_info(agp_kern_info * info) { memset(info, 0, sizeof(agp_kern_info)); @@ -378,7 +382,7 @@ } info->version.major = agp_bridge.version->major; info->version.minor = agp_bridge.version->minor; - info->device = agp_bridge.dev; + info->device = agp_bridge.dev ? agp_bridge.dev : &fake_bridge_dev; info->chipset = agp_bridge.type; info->mode = agp_bridge.mode; info->aper_base = agp_bridge.gart_bus_addr; @@ -4531,6 +4535,8 @@ #define log2(x) ffz(~(x)) #endif +#define HP_ZX1_IOC_OFFSET 0x1000 /* ACPI reports SBA, we want IOC */ + #define HP_ZX1_IOVA_BASE GB(1UL) #define HP_ZX1_IOVA_SIZE GB(1UL) #define HP_ZX1_GART_SIZE (HP_ZX1_IOVA_SIZE / 2) @@ -4546,8 +4552,8 @@ }; static struct _hp_private { - struct pci_dev *ioc; - volatile u8 *registers; + volatile u8 *ioc_regs; + volatile u8 *lba_regs; u64 *io_pdir; // PDIR for entire IOVA u64 *gatt; // PDIR just for GART (subset of above) u64 gatt_entries; @@ -4574,7 +4580,7 @@ * - IOVA space is 1Gb in size * - first 512Mb is IOMMU, second 512Mb is GART */ - hp->io_tlb_ps = INREG64(hp->registers, HP_ZX1_TCNFG); + hp->io_tlb_ps = INREG64(hp->ioc_regs, HP_ZX1_TCNFG); switch (hp->io_tlb_ps) { case 0: hp->io_tlb_shift = 12; break; case 1: hp->io_tlb_shift = 13; break; @@ -4590,13 +4596,13 @@ hp->io_page_size = 1 << hp->io_tlb_shift; hp->io_pages_per_kpage = PAGE_SIZE / hp->io_page_size; - hp->iova_base = INREG64(hp->registers, HP_ZX1_IBASE) & ~0x1; + hp->iova_base = INREG64(hp->ioc_regs, HP_ZX1_IBASE) & ~0x1; hp->gart_base = hp->iova_base + HP_ZX1_IOVA_SIZE - HP_ZX1_GART_SIZE; hp->gart_size = HP_ZX1_GART_SIZE; hp->gatt_entries = hp->gart_size / hp->io_page_size; - hp->io_pdir = phys_to_virt(INREG64(hp->registers, HP_ZX1_PDIR_BASE)); + hp->io_pdir = phys_to_virt(INREG64(hp->ioc_regs, HP_ZX1_PDIR_BASE)); hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) { @@ -4645,38 +4651,18 @@ return 0; } -static int __init hp_zx1_ioc_init(void) +static int __init hp_zx1_ioc_init(u64 ioc_hpa, u64 lba_hpa) { struct _hp_private *hp = &hp_private; - struct pci_dev *ioc; - int i; - ioc = pci_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_ZX1_IOC, NULL); - if (!ioc) { - printk(KERN_ERR PFX "Detected HP ZX1 AGP bridge but no IOC\n"); - return -ENODEV; - } - hp->ioc = ioc; - - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - if (pci_resource_flags(ioc, i) == IORESOURCE_MEM) { - hp->registers = (u8 *) ioremap(pci_resource_start(ioc, - i), - pci_resource_len(ioc, i)); - break; - } - } - if (!hp->registers) { - printk(KERN_ERR PFX "Detected HP ZX1 AGP bridge but no CSRs\n"); - - return -ENODEV; - } + hp->ioc_regs = ioremap(ioc_hpa, 1024); + hp->lba_regs = ioremap(lba_hpa, 256); /* * If the IOTLB is currently disabled, we can take it over. * Otherwise, we have to share with sba_iommu. */ - hp->io_pdir_owner = (INREG64(hp->registers, HP_ZX1_IBASE) & 0x1) == 0; + hp->io_pdir_owner = (INREG64(hp->ioc_regs, HP_ZX1_IBASE) & 0x1) == 0; if (hp->io_pdir_owner) return hp_zx1_ioc_owner(); @@ -4699,19 +4685,17 @@ struct _hp_private *hp = &hp_private; agp_bridge.gart_bus_addr = hp->gart_base; - agp_bridge.capndx = pci_find_capability(agp_bridge.dev, PCI_CAP_ID_AGP); - pci_read_config_dword(agp_bridge.dev, - agp_bridge.capndx + PCI_AGP_STATUS, &agp_bridge.mode); + agp_bridge.mode = INREG32(hp->lba_regs, HP_ZX1_AGP_STATUS); if (hp->io_pdir_owner) { - OUTREG64(hp->registers, HP_ZX1_PDIR_BASE, + OUTREG64(hp->ioc_regs, HP_ZX1_PDIR_BASE, virt_to_phys(hp->io_pdir)); - OUTREG64(hp->registers, HP_ZX1_TCNFG, hp->io_tlb_ps); - OUTREG64(hp->registers, HP_ZX1_IMASK, ~(HP_ZX1_IOVA_SIZE - 1)); - OUTREG64(hp->registers, HP_ZX1_IBASE, hp->iova_base | 0x1); - OUTREG64(hp->registers, HP_ZX1_PCOM, + OUTREG64(hp->ioc_regs, HP_ZX1_TCNFG, hp->io_tlb_ps); + OUTREG64(hp->ioc_regs, HP_ZX1_IMASK, ~(HP_ZX1_IOVA_SIZE - 1)); + OUTREG64(hp->ioc_regs, HP_ZX1_IBASE, hp->iova_base | 0x1); + OUTREG64(hp->ioc_regs, HP_ZX1_PCOM, hp->iova_base | log2(HP_ZX1_IOVA_SIZE)); - INREG64(hp->registers, HP_ZX1_PCOM); + INREG64(hp->ioc_regs, HP_ZX1_PCOM); } return 0; @@ -4722,17 +4706,18 @@ struct _hp_private *hp = &hp_private; if (hp->io_pdir_owner) - OUTREG64(hp->registers, HP_ZX1_IBASE, 0); - iounmap((void *) hp->registers); + OUTREG64(hp->ioc_regs, HP_ZX1_IBASE, 0); + iounmap((void *) hp->ioc_regs); + iounmap((void *) hp->lba_regs); } static void hp_zx1_tlbflush(agp_memory * mem) { struct _hp_private *hp = &hp_private; - OUTREG64(hp->registers, HP_ZX1_PCOM, + OUTREG64(hp->ioc_regs, HP_ZX1_PCOM, hp->gart_base | log2(hp->gart_size)); - INREG64(hp->registers, HP_ZX1_PCOM); + INREG64(hp->ioc_regs, HP_ZX1_PCOM); } static int hp_zx1_create_gatt_table(void) @@ -4838,7 +4823,22 @@ return HP_ZX1_PDIR_VALID_BIT | addr; } -static int __init hp_zx1_setup (struct pci_dev *pdev) +static void hp_zx1_agp_enable(u32 mode) +{ + struct _hp_private *hp = &hp_private; + u32 command; + + command = INREG32(hp->lba_regs, HP_ZX1_AGP_STATUS); + + command = agp_collect_device_status(mode, command); + command |= 0x00000100; + + OUTREG32(hp->lba_regs, HP_ZX1_AGP_COMMAND, command); + + agp_device_command(command, 0); +} + +static int __init hp_zx1_setup(u64 ioc_hpa, u64 lba_hpa) { agp_bridge.dev_private_data = NULL; agp_bridge.size_type = FIXED_APER_SIZE; @@ -4848,7 +4848,7 @@ agp_bridge.cleanup = hp_zx1_cleanup; agp_bridge.tlb_flush = hp_zx1_tlbflush; agp_bridge.mask_memory = hp_zx1_mask_memory; - agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.agp_enable = hp_zx1_agp_enable; agp_bridge.cache_flush = global_cache_flush; agp_bridge.create_gatt_table = hp_zx1_create_gatt_table; agp_bridge.free_gatt_table = hp_zx1_free_gatt_table; @@ -4859,12 +4859,61 @@ agp_bridge.agp_alloc_page = agp_generic_alloc_page; agp_bridge.agp_destroy_page = agp_generic_destroy_page; agp_bridge.cant_use_aperture = 1; + agp_bridge.type = HP_ZX1; - return hp_zx1_ioc_init(); + fake_bridge_dev.vendor = PCI_VENDOR_ID_HP; + fake_bridge_dev.device = PCI_DEVICE_ID_HP_ZX1_LBA; - (void) pdev; /* unused */ + return hp_zx1_ioc_init(ioc_hpa, lba_hpa); } +static int __init acpi_hp_zx1_gart_add(struct acpi_device *device) +{ + acpi_handle handle, parent; + acpi_status status; + acpi_device_info info; + u64 lba_hpa, sba_hpa, length; + + status = acpi_hp_csr_space(device->handle, &lba_hpa, &length); + if (ACPI_FAILURE(status)) + return 1; + + /* Look for an enclosing IOC scope and find its CSR space */ + handle = device->handle; + do { + status = acpi_get_object_info(handle, &info); + if (ACPI_SUCCESS(status)) { + /* TBD check _CID also */ + info.hardware_id[sizeof(info.hardware_id)-1] = '\0'; + if (!strcmp(info.hardware_id, "HWP0001")) { + status = acpi_hp_csr_space(handle, &sba_hpa, + &length); + if (ACPI_SUCCESS(status)) + break; + else { + printk(KERN_ERR PFX "Detected HP ZX1 " + "AGP LBA but no IOC.\n"); + return 1; + } + } + } + + status = acpi_get_parent(handle, &parent); + handle = parent; + } while (ACPI_SUCCESS(status)); + + if (hp_zx1_setup(sba_hpa + HP_ZX1_IOC_OFFSET, lba_hpa)) + return 1; + + return 0; +} + +static struct acpi_driver acpi_hp_zx1_gart_driver = { + .name = "HP ZX1 GART Driver", + .ids = "HWP0003", + .ops = { .add = acpi_hp_zx1_gart_add }, +}; + #endif /* CONFIG_AGP_HP_ZX1 */ /* per-chipset initialization data. @@ -5207,15 +5256,6 @@ via_generic_setup }, #endif /* CONFIG_AGP_VIA */ -#ifdef CONFIG_AGP_HP_ZX1 - { PCI_DEVICE_ID_HP_ZX1_LBA, - PCI_VENDOR_ID_HP, - HP_ZX1, - "HP", - "ZX1", - hp_zx1_setup }, -#endif - { 0, }, /* dummy final entry, always present */ }; @@ -5314,6 +5354,16 @@ struct pci_dev *dev = NULL; u8 cap_ptr = 0x00; +#ifdef CONFIG_AGP_HP_ZX1 + int rc; + + rc = acpi_bus_register_driver(&acpi_hp_zx1_gart_driver); + if (rc > 0) { + printk(KERN_INFO PFX "Detected an HP ZX1 Chipset.\n"); + return 0; + } +#endif + /* * Some systems have multiple host bridges, so * we can't just use the first one we find. @@ -5481,23 +5531,6 @@ #endif /* CONFIG_AGP_SWORKS */ -#ifdef CONFIG_AGP_HP_ZX1 - if (dev->vendor == PCI_VENDOR_ID_HP) { - do { - /* ZX1 LBAs can be either PCI or AGP bridges */ - if (pci_find_capability(dev, PCI_CAP_ID_AGP)) { - printk(KERN_INFO PFX "Detected HP ZX1 AGP " - "chipset at %s\n", dev->slot_name); - agp_bridge.type = HP_ZX1; - agp_bridge.dev = dev; - return hp_zx1_setup(dev); - } - dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, dev); - } while (dev); - return -ENODEV; - } -#endif /* CONFIG_AGP_HP_ZX1 */ - /* find capndx */ cap_ptr = pci_find_capability(dev, PCI_CAP_ID_AGP); if (cap_ptr == 0x00) @@ -5704,7 +5737,8 @@ inter_module_register("drm_agp", THIS_MODULE, &drm_agp); - pm_register(PM_PCI_DEV, PM_PCI_ID(agp_bridge.dev), agp_power); + if (agp_bridge.dev) + pm_register(PM_PCI_DEV, PM_PCI_ID(agp_bridge.dev), agp_power); return 0; }